#include "helper.h"
#include "monitor.h"
#include "reg.h"

extern uint32_t instr;
extern char assembly[80];

/* decode R-type instrucion */
static void decode_r_type(uint32_t instr) {

	op_src1->type = OP_TYPE_REG;
	op_src1->reg = (instr & RS_MASK) >> (RT_SIZE + IMM_SIZE);
	op_src1->val = reg_w(op_src1->reg);
	
	op_src2->type = OP_TYPE_REG;
	op_src2->imm = (instr & RT_MASK) >> (RD_SIZE + SHAMT_SIZE + FUNC_SIZE);
	op_src2->val = reg_w(op_src2->reg);

	op_dest->type = OP_TYPE_REG;
	op_dest->reg = (instr & RD_MASK) >> (SHAMT_SIZE + FUNC_SIZE);
}

static void decode_trap_type(uint32_t instr) {

	op_src1->type = OP_TYPE_IMM;
	op_src1->imm = (instr & CODE_MASK) >> FUNC_SIZE;
	op_src1->val = op_src1->imm;
}

static void decode_privilege_type(uint32_t instr) {

	op_src1->type = OP_TYPE_IMM;
	op_src1->imm = (instr & RS_MASK) >> (RT_SIZE + IMM_SIZE);
	op_src1->val = op_src1->imm;
	
	op_src2->type = OP_TYPE_REG;
	op_src2->imm = (instr & RT_MASK) >> (RD_SIZE + SHAMT_SIZE + FUNC_SIZE);
	op_src2->val = reg_w(op_src2->reg);

	op_dest->type = OP_TYPE_REG;
	op_dest->reg = (instr & RD_MASK) >> (SHAMT_SIZE + FUNC_SIZE);
	op_dest->val = reg_w(op_dest->reg);
}

make_helper(and) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = (op_src1->val & op_src2->val);
	sprintf(assembly, "and   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(nor) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = ~(op_src1->val | op_src2->val);
	sprintf(assembly, "nor   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(or) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = (op_src1->val | op_src2->val);
	sprintf(assembly, "or   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(xor) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = (op_src1->val ^ op_src2->val);
	sprintf(assembly, "xor   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}


make_helper(add) {

	decode_r_type(instr);

	int temp = op_src1->val + op_src2->val;
	int sgn_1 = op_src1->val >> 31;
	int sgn_2 = op_src2->val >> 31;
	int sgn_t = temp >> 31;
	int str_offset = 0;

	if(sgn_1 == sgn_2 && sgn_1 != sgn_t)  // ov exception
	{
		cp0_w(R_CAUSE) = (cp0_w(R_CAUSE) & (~EXCCODE_MASK)) | (Ov << 2);
		cp0_w(R_EPC) = cpu.pc;
		cp0_w(R_STATUS) = cp0_w(R_STATUS) | EXL_MASK;
		if(temu_state == JUMP)	// delay slot
		{
			cp0_w(R_CAUSE) = cp0_w(R_CAUSE) | BD_MASK;
			cp0_w(R_EPC) -= 4;
		}
		str_offset = sprintf(assembly, "Ov exception occured.\n");
	}
	reg_w(op_dest->reg) = temp;
	sprintf(assembly + str_offset, "add   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(addu) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = op_src1->val + op_src2->val;
	sprintf(assembly, "addu   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(sub) {

	decode_r_type(instr);

	int temp = op_src1->val - op_src2->val;
	int sgn_1 = op_src1->val >> 31;
	int sgn_2 = op_src2->val >> 31;
	int sgn_t = temp >> 31;

	if(sgn_1 == sgn_2 && sgn_1 != sgn_t)
	{
		// TODO, 触发整型溢出例外
	}
	reg_w(op_dest->reg) = temp;
	sprintf(assembly, "sub   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(subu) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = op_src1->val - op_src2->val;
	sprintf(assembly, "subu   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(slt) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = ((int)op_src1->val) < ((int)op_src2->val);
	sprintf(assembly, "slt   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(sltu) {

	decode_r_type(instr);
	reg_w(op_dest->reg) = op_src1->val < op_src2->val;
	sprintf(assembly, "sltu   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(div_) {

	decode_r_type(instr);
	cpu.hi = ((int)op_src1->val) / ((int)op_src2->val);
	cpu.lo = ((int)op_src1->val) % ((int)op_src2->val);
	sprintf(assembly, "div   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(divu) {

	decode_r_type(instr);
	cpu.hi = (uint32_t)(op_src1->val / op_src2->val);
	cpu.lo = (uint32_t)(op_src1->val % op_src2->val);
	sprintf(assembly, "divu   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(mult) {

	decode_r_type(instr);
	uint64_t temp = (uint64_t)((int)op_src1->val * (int)op_src2->val);
	cpu.hi = (temp >> 32) & 0xFFFFFFFF;
	cpu.lo = ((temp << 32) >> 32) & 0xFFFFFFFF;
	sprintf(assembly, "mult   %s,   %s", REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(multu) {

	decode_r_type(instr);
	uint64_t temp = (uint64_t)op_src1->val * (uint64_t)op_src2->val;
	cpu.hi = (temp >> 32) & 0xFFFFFFFF;
	cpu.lo = ((temp << 32) >> 32) & 0xFFFFFFFF;
	sprintf(assembly, "multu   %s,   %s", REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(sllv) {

	decode_r_type(instr);
    reg_w(op_dest->reg) = op_src2->val << (((op_src1->val << 27) >> 27) & 0x1F);
    sprintf(assembly, "sllv   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src2->reg), REG_NAME(op_src1->reg));
}

make_helper(sll) {

    decode_r_type(instr);
    reg_w(op_dest->reg) = op_src2->val << ((instr >> 6) & 0x1F);
    sprintf(assembly, "sll   %s,   %s,   %d", REG_NAME(op_dest->reg), REG_NAME(op_src2->reg), (((op_src1->val << 6) >> 6) & 0x1F));
}	// 不确定

make_helper(srav) {

	decode_r_type(instr);
    reg_w(op_dest->reg) = (int)op_src2->val >> (((op_src1->val << 27) >> 27) & 0x1F);
    sprintf(assembly, "srav   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src2->reg), REG_NAME(op_src1->reg));
}

make_helper(sra) {

    decode_r_type(instr);
    reg_w(op_dest->reg) = (int)op_src2->val >> ((instr >> 6) & 0x1F);
    sprintf(assembly, "sra   %s,   %s,   %d", REG_NAME(op_dest->reg), REG_NAME(op_src2->reg), (((op_src1->val << 6) >> 6) & 0x1F));
}	// 不确定

make_helper(srlv) {

	decode_r_type(instr);
    reg_w(op_dest->reg) = (op_src2->val >> (((op_src1->val << 27) >> 27) & 0x1F));
    sprintf(assembly, "sllv   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src2->reg), REG_NAME(op_src1->reg));
}

make_helper(srl) {

    decode_r_type(instr);
    reg_w(op_dest->reg) = op_src2->val >> ((instr >> 6) & 0x1F);
    sprintf(assembly, "sra   %s,   %s,   %d", REG_NAME(op_dest->reg), REG_NAME(op_src2->reg), (((op_src1->val << 6) >> 6) & 0x1F));
}	// 不确定

make_helper(jr) {

    decode_r_type(instr);
    cpu.next_pc = op_src1->val - 4;
    sprintf(assembly, "jr   %s", REG_NAME(op_src1->reg));
    temu_state = JUMP;
}

make_helper(jalr) {

    decode_r_type(instr);
    reg_w(op_dest->reg) = cpu.pc + 8;
    cpu.next_pc = op_src1->val - 4;
    sprintf(assembly, "jalr   %s,   %s", REG_NAME(op_src1->reg), REG_NAME(op_dest->reg));
    temu_state = JUMP;
}

make_helper(mfhi) {

    decode_r_type(instr);
    reg_w(op_dest->reg) = cpu.hi;
    sprintf(assembly, "mfhi   %s", REG_NAME(op_dest->reg));
}

make_helper(mflo) {

    decode_r_type(instr);
    reg_w(op_dest->reg) = cpu.lo;
    sprintf(assembly, "mflo   %s", REG_NAME(op_dest->reg));
}

make_helper(mthi) {

    decode_r_type(instr);
    cpu.hi = op_src1->val;
    sprintf(assembly, "mthi   %s", REG_NAME(op_src1->reg));
}

make_helper(mtlo) {

    decode_r_type(instr);
    cpu.lo = op_src1->val;
    sprintf(assembly, "mtlo   %s", REG_NAME(op_src1->reg));
}

make_helper(break_) {

    decode_trap_type(instr);
    cp0_w(R_CAUSE) = (cp0_w(R_CAUSE) & (~EXCCODE_MASK)) | (Bp << 2);
    cp0_w(R_EPC) = cpu.pc;
    cp0_w(R_STATUS) = cp0_w(R_STATUS) | EXL_MASK;
    if(temu_state == JUMP)	// delay slot
    {
	cp0_w(R_CAUSE) = cp0_w(R_CAUSE) | BD_MASK;
	cp0_w(R_EPC) -= 4;
    }
    sprintf(assembly, "break");
}

make_helper(syscall_) {

    decode_trap_type(instr);
    cp0_w(R_CAUSE) = (cp0_w(R_CAUSE) & (~EXCCODE_MASK)) | (Sys << 2);
    cp0_w(R_EPC) = cpu.pc;
    cp0_w(R_STATUS) = cp0_w(R_STATUS) | EXL_MASK;
    if(temu_state == JUMP)	// delay slot
    {
	cp0_w(R_CAUSE) = cp0_w(R_CAUSE) | BD_MASK;
	cp0_w(R_EPC) -= 4;
    }
    sprintf(assembly, "syscall");
}

make_helper(mc0) {

    if((instr >> 25) & 0x1)  // eret
    {
	cpu.pc = cp0_w(R_EPC) - 4;
	sprintf(assembly, "eret");
    }
    else
    {
	decode_privilege_type(instr);
	if(op_src1->val == 0)  // mfc0
	{
            reg_w(op_src2->reg) = cp0_w(op_dest->reg);
	    sprintf(assembly, "mfc0   %s,   %s", REG_NAME(op_src2->reg), CP0_NAME(op_dest->reg));
	}
	else  // mtc0
	{
	    cp0_w(op_dest->reg) = reg_w(op_src2->reg);
	    sprintf(assembly, "mtc0   %s,   %s", REG_NAME(op_src2->reg), CP0_NAME(op_dest->reg));
	}
    }
	
	
}